#pragma once
#include <iostream>
#include <cstdlib>

//#include "LocalData.hpp"
#include "GetInput.hpp"
//#include "ChatClient.hpp"
#define DRAW_THREADS 4

class ChatWindow;

struct CaseTool{
    CaseTool(ChatWindow* tcw, int i)
      :cw(tcw), num(i) {}
    ChatWindow* cw;
    int num;
};

int is_recv = 0;

class ChatWindow{
  public:
    ChatWindow()
    :is_exit(0)
    ,header(nullptr)
    ,msg_show(nullptr)
    ,user_list(nullptr)
    ,input(nullptr){
      pthread_mutex_init(&wre_lock, nullptr);
    }

    ~ChatWindow(){
      if(header){
        delwin(header);
      }
      if(msg_show){
        delwin(msg_show);
      }
      if(user_list){
        delwin(user_list);
      }
      if(input){
        delwin(input);
      }
      endwin();
      pthread_mutex_destroy(&wre_lock);
    }

    void InitWin(){
      initscr();
      curs_set(2);
      cbreak();
      nonl();
      noecho();
    }

    void SafeWreFresh(WINDOW* win){
      pthread_mutex_lock(&wre_lock);
      wrefresh(win);
      pthread_mutex_unlock(&wre_lock);
    }

    void LogAndRegiste(){
      cli.Start();
    }

    void Start(){
      for(int i = 0; i < DRAW_THREADS; ++i){
        CaseTool* ct = new CaseTool(this, i);
        int ret = pthread_create(&pid[i], nullptr, DrawWindow, ct);
        if(ret < 0){
          perror("pthread_create");
          exit(1);
        }
      }
      for(int i = 0; i < DRAW_THREADS; ++i){
        pthread_join(pid[i], nullptr);
      }
    }

    void BoxHeader(){
      int row = LINES / 5;
      int col = COLS;
      int start_r = 0;
      int start_c = 0;
      header = newwin(row, col, start_r, start_c);
      box(header, 0, 0);
      SafeWreFresh(header);
    }

    void BoxMessage(){
      int row = LINES * 3 / 5;
      int col = COLS * 3 / 4;
      int start_r = LINES / 5;
      int start_c = 0;
      msg_show = newwin(row, col, start_r, start_c);
      box(msg_show, 0, 0);
      SafeWreFresh(msg_show);
    }

    void BoxUserList(){
      int row = LINES * 3 / 5;
      int col = COLS / 4;
      int start_r = LINES / 5;
      int start_c = COLS * 3 / 4;
      user_list = newwin(row, col, start_r, start_c);
      box(user_list, 0, 0);
      SafeWreFresh(user_list);
    }

    void BoxInput(){ 
      int row = LINES / 5;
      int col = COLS;
      int start_r = LINES * 4 / 5;
      int start_c = 0;
      input = newwin(row, col, start_r, start_c);
      box(input, 0, 0);
      SafeWreFresh(input);
    }

    void DrawHeader(){
      int r, c;
      BoxHeader();
      getmaxyx(header, r, c);
      const char welcome[] = "欢迎使用憨憨聊天~";
      r /= 2;
      c = (c - sizeof(welcome) + 1) / 2;
      mvwaddstr(header, r, c, welcome);
      SafeWreFresh(header);
    }

    void DrawMessage(){
      while(!is_exit){
        BoxMessage();
        int y = 1, x = 1;
        for(const auto& e : msg_list.ShowMsg()){
          mvwaddstr(msg_show, y++, x, e.data());
        }
        SafeWreFresh(msg_show);
        RecvMsg();
      }
    }

    void DrawUserList(){
      while(!is_exit){
        std::string hint = "在线成员:";
        int y = 1, x = 1;
        char tmp[128] = {0};
        BoxUserList();
        sprintf(tmp, "%ld", online_user.size());
        hint += tmp;
        mvwaddstr(user_list, ++y, x, hint.data());
        for(const auto& e : online_user){
          mvwaddstr(user_list, ++y, x, e.second.data());
        }
        SafeWreFresh(user_list);
        sleep(2);
      }
    }

    void DrawInput(){
      int r, c;
      r = 1;
      c = 1;
      while(!is_exit){
        BoxInput();
        std::string msg;
        while(!is_exit && msg.empty()){
          pthread_mutex_lock(&wre_lock);
          wmove(input, r, c);
          pthread_mutex_unlock(&wre_lock);
          GetInput(msg, input, r, c, &wre_lock, is_exit);
        }
        if(is_exit){
          cli.OutLine();
        }
        cli.SendMsg(msg);
      }
    }

    static void* DrawWindow(void* arg){
      CaseTool* ct = (CaseTool*)arg;
      ChatWindow* cw = ct->cw;
      int num = ct->num;
      delete ct;
      switch(num){
        case 0:
          cw->DrawHeader();
          break;
        case 1:
          cw->DrawMessage();
          break;
        case 2:
          cw->DrawUserList();
          break;
        case 3:
          cw->DrawInput();
          break;
        default:
          break;
      }
      return nullptr;
    }

    void RecvMsg(){
      std::string recv_msg;
      cli.RecvMsg(recv_msg);
      Message msg;
      msg.Deserialize(recv_msg);
      if(msg.GetStatus() == OUTLINE){
        online_user.erase(msg.GetID());
        return;
      }
      else if(online_user.find(msg.GetID()) == online_user.end()){
        std::string info;
        info += msg.GetInfo(NICKNAME);
        info += "-";
        info += msg.GetInfo(SCHOOL);
        online_user[msg.GetID()] = info;
        if(msg.GetInfo(MESSAGE).empty()){
          return;
        }
      }
      std::string info;
      info += msg.GetInfo(NICKNAME);
      info += "-";
      info += msg.GetInfo(SCHOOL);
      msg_list.PushInfo(info);
      msg_list.PushMsg(msg.GetInfo(MESSAGE));
    }
  private:
    int is_exit;
    WINDOW* header;
    WINDOW* msg_show;
    WINDOW* user_list;
    WINDOW* input;
    pthread_t pid[DRAW_THREADS];
    pthread_mutex_t wre_lock;
    //Client cli;
    //MessageList msg_list;
    //std::unordered_map<ID_Type, std::string> online_user;
};
